/******************************************************************************/
/* 				MAIN.C								*/
/* MINIX KERNEL UDP SERVER listening on MOLKERNEL_ADDR, MOLKERNEL_PORT		*/
/* MOLKERNEL starts IPC SERVER as thread  listening on (MOLKERNEL_PORT+1) 	*/
/*													*/
/* MOLKERNEL: Accepts BIND and UNBIND request from process and manages the 	*/
/* kernel process table named kproc[]							*/
/* Every (pseudo) Minix process open 2 communications channels with MOLKERNEL */
/*  Control Channel: to communicate with MOLKERNEL and SYSTASK			*/
/*  IPC Channel: to transfer MINIX messages inside a MOL message			*/
/******************************************************************************/

/*---------------------- LINUX HEADERS ---------------------------*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <sys/syslog.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*---------------------- MINIX HEADERS ---------------------------*/
#include "../include/minix/config.h" 	
#include "../include/minix/endpoint.h"
#include "../include/minix/com.h"
#include "../../kernel/ipc.h"	

/* #include "../../kernel/config.h" 	*/
/* #include "../../kernel/const.h" 		*/
/* #include "../../kernel/type.h" 		*/

/*---------------------- MOL HEADERS -----------------------------*/
#include "../include/mol/molminix.h"
#define MOLDBG 1
#include "../include/mol/mollib.h"

#define _SYSTEM
#include "../include/mol/molerr.h" 

/*---------------------- MOLKERNEL HEADERS -------------------------*/
#include "molproc.h"
#include "svripc.h"
#include "molclock.h"
#include "molsystem.h"
#include "molipcsvr.h"

/*------------------- GLOBAL VARIABLES ----------------------------*/
ipc_t 			ctrl_ipc;		/* Ctrl IPC struct			*/
pid_t			kpid;			/* PID of MOLKERNEL		*/
char 			copybuf[MAX_BLOCK_SIZE];		/*  COPYIN/COPYOUT buffer pointer */

extern ipc_t 	ipcs_ipc;		/*  IPC server  IPC struct		*/
struct kproc	kproc[NR_PROCS+NR_TASKS];	/* kernel process table */
struct priv 	priv[NR_SYS_PROCS];		/* system properties table */
struct priv 	*ppriv_addr[NR_SYS_PROCS];	/* direct slot pointers */

name2nr_t name2nr[] = {   /* table to match special MoL process to process numbers */
/* process nr,   name */ 
 { IDLE,   "idle"  },
 { CLOCK, "clock" },
 { SYSTEM, "system"},
 { HARDWARE, "kernel"},
 { PM_PROC_NR, "pm"},
 { FS_PROC_NR, "fs"    },
 { RS_PROC_NR, "rs"    },
 { DS_PROC_NR, "ds"    },
 { TTY_PROC_NR, "tty"   },
 { MEM_PROC_NR, "mem"   },
 { LOG_PROC_NR,"log"   },
 { INIT_PROC_NR, "init"  },
};

pthread_t syst_tid;			/* systask thread ID			*/
pthread_t ipcs_tid;			/* IPC server thread ID			*/

/*------------------------------- LOCAL FUNCTION DECLARATIONS ------------------*/
int init_kernel(void);
int  do_bind(ipc_t *pipc); 
int do_unbind(ipc_t *pipc);
int do_copyin(ipc_t *pipc);
int do_copyout(ipc_t *pipc);

int get_p_nr(ipc_t *pipc);
void sigusr1(int sig);
void init_priv(void);  

/******************************************************************/
/*				MOLKERNEL						*/			
/******************************************************************/

int main(int argc,char **argv) 
{
	int rcode;
	int rqst;
 
 /*
 	openlog("molkernel", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_LOCAL0);
	syslog(LOG_INFO,"start ");  
*/	
	init_kernel();
   	
	while (TRUE) 
		{
OLPRINT("\n\nWaiting Request message...\n");
       	rcode = svr_rcv(&ctrl_ipc);
       	if ( rcode < 0 )
       	    MOLERROR(" svr_rcv rcode=%d",rcode);

OLDEBUG("Request: code=%X spid=%d seq=%d\n",
			ctrl_ipc.molmsg.code,
			ctrl_ipc.molmsg.spid,
			ctrl_ipc.molmsg.seq);

		rqst 	= ctrl_ipc.molmsg.code; 
		switch(rqst)
           		{
/*--------------------------- FUNCIONES DE MOL --------------*/
            	case BIND: 	/* register minix process	*/
					rcode = do_bind(&ctrl_ipc);
                	break;
           		case UNBIND: /* unregister minix process 	*/
					rcode = do_unbind(&ctrl_ipc);
					break;
				case (HELLO | ACKNOWLEDGE): 
					MOLPRINT("HELLO ACK received\n");
					break;			
				case (COPYIN | ACKNOWLEDGE): 
					MOLPRINT("COPYIN ACK received\n");
					rcode = ctrl_ipc.molmsg.mnxmsg.m_type;
					if( rcode != OK)
						MOLPRINT("COPYIN ACK ERROR rcode=%d\n",rcode);
					else
						do_copyin(&ctrl_ipc);
					break;
				case (COPYOUT | ACKNOWLEDGE): 
					MOLPRINT("COPYOUT ACK received\n");
					rcode = ctrl_ipc.molmsg.mnxmsg.m_type;
					if( rcode != OK)
						MOLPRINT("COPYOUT ACK ERROR rcode=%d\n",rcode);
					else
						do_copyout(&ctrl_ipc);
					break;
/* FUTURE ...................................................
               	case ADDMEMBER: add other MOLKERNEL group member
				do_addmember();
                        break;
                  case DELMEMBER: remove other MOLKERNEL group member
				do_delmember();
                        break;
.............................................................*/
            	default:
					do_badrqst(&ctrl_ipc); 
					break;
            	}
		}
		
	rcode = mol_ipc_end(&ctrl_ipc);
	if (rcode != 0)
		MOLERROR("mol_ipc_end rcode=%d\n",rcode);
	closelog();
}

/*----------------------------------------------------------*/
/*			init_kernel						*/
/* Initialize MOLKERNEL							*/
/*----------------------------------------------------------*/
int init_kernel(void)
{
	int i, rcode;
	struct kproc *pptr;
	int 	idx;
	struct sigaction sa;


	/* Initializa Control and IPC channels			*/
	rcode = svr_open(&ctrl_ipc, &ipcs_ipc);
	if (rcode != OK)
		MOLERROR("svr_open rcode=%d",rcode);

	/* Get MOLKERNEL PID						*/
	kpid = getpid();	

	/* FUTURO: Get MOLKERNEL NODE ID				*/
	/* knid = getnid();						*/

	/* FUTURO: Get KERNELS GROUP ID				*/
	/* kgid = getkgid(knid);					*/

	/* FUTURO: Get MOLKERNEL MEMBER ID				*/
	/* kmid = getmid(kgid);						*/

	/* FUTURO: BROADCASTEAR ADDMEMBER				*/
	/* nmbrs = open_kgroup(kgid, kmid);				*/

	/* Initialize the kernel process table			*/
	for (i = -NR_TASKS; i < NR_PROCS; ++i) 
		{
		pptr= &kproc[i+NR_TASKS];
		pptr->p_rts_flags		= SLOT_FREE;
		pptr->p_nr 			= i;
		pptr->p_endpoint		= _ENDPOINT(0, i); /* GENERATION=0 */
	/* 	pptr->nid 			= getnid();	FUTURO NODE ID	*/
		pptr->pseq 			= 0;
		pptr->kseq			= 0;
		pptr->p_dead		= 0;
		pptr->p_hello		= 0;
		pptr->p_caller_q	= NIL_PROC;
		pptr->p_q_link		= NIL_PROC;
		pptr->p_getfrom_e	= NONE;
		pptr->p_sendto_e	= NONE;
		}

	/*---------------- init  Priv structures  -------------------------*/
	init_priv();

	/*---------------- Start IPC server thread  ----------------------*/
	rcode = pthread_create(&ipcs_tid, NULL, ipcsvr_thread, &idx);
	if (rcode != 0)
		MOLERROR("pthread_create rcode=%d\n",rcode);
	
	/*---------------- Start SYSTASK/CLOCK thread  ----------------------*/
	rcode = pthread_create(&syst_tid, NULL, systask_thread, &idx);
	if (rcode != 0)
		MOLERROR("pthread_create rcode=%d\n",rcode);
	
		/*------- Sets the Signal Action for SIGURSR1---------*/
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESTART;
	sa.sa_handler = sigusr1;
	if (sigaction(SIGUSR1, &sa, NULL) == -1)
		MOLERROR("sigaction for SIGUSR1\n", -1);
		
OLPRINT("MOLKERNEL IPC server initialized (PID=%d)\n",kpid);

}

/*----------------------------------------------------------*/
/*			do_bind						*/
/* BIND request received						*/
/* Every process must register with MOLKERNEL to use MOL IPC*/
/*----------------------------------------------------------*/
int do_bind(ipc_t *pipc)
{
	int rcode, p_nr, gen;
	struct kproc *pptr;

OLDEBUG("oper=%X spid=%d seq=%d\n",
                pipc->molmsg.code,
                pipc->molmsg.spid,
                pipc->molmsg.seq);

	/* The sequence number of a BIND message must be 0	*/
	if( pipc->molmsg.seq != 0)
		{
	      pipc->molmsg.code = EMOLRANGE;
	      pipc->molmsg.spid = kpid;
		rcode = svr_send(pipc);
		if( rcode != OK )
			MOLERROR("svr_send rcode=%d\n",rcode );                      
		MOLPRINT("sequence # not zero: seq=%d \n", pipc->molmsg.seq );
		MOLERROR("Bad sequence: code=%d\n",EMOLRANGE);
		}

	/* returns a free slot of the process table or the special slot 	*/
	/* for special MoL process							*/
	p_nr = get_p_nr(pipc);
	if ( p_nr < NR_PROCS ) /* encontro p_nr libre */
		{
		/* Sets the initial status of a process */
		pptr = &kproc[p_nr+NR_TASKS];
		pptr->p_rts_flags = 0;
		pptr->pid 		= pipc->molmsg.spid;
	/*	pptr->nid 		= pipc->molmsg.spid;	FUTURE	 */
		pptr->p_nr	 	= p_nr;	
		pptr->kseq 		= 0;		
		pptr->pseq 		= 1;
		pptr->p_caller_q	= NIL_PROC;
		pptr->p_q_link	= NIL_PROC;
		pptr->p_getfrom_e	= NONE;
		pptr->p_sendto_e	= NONE;

		/* Checks if it is a special process or ordinary one	*/
		if( p_nr > INIT_PROC_NR)	/* ordinary process	*/
			{ 
			gen = _ENDPOINT_G(pptr->p_endpoint);
			if(++gen >= _ENDPOINT_MAX_GENERATION)	/* increase generation */
				gen = 1;					/* generation number wraparound */
			pptr->p_endpoint 	= _ENDPOINT(gen, p_nr);
			}
		else
			pptr->p_endpoint 	= _ENDPOINT(0, p_nr);	/* Generation 0 	*/

		/* copy the name of the process to the process slot			*/
		strncpy((char*)&pptr->p_name, (char*)&pipc->molmsg.mnxmsg.m_u.m_m3.m3ca1,P_NAME_LEN);

OLDEBUG("Binding pid=%d p_endpoint=%d p_nr=%d p_name=%s \n",
		pptr->pid,
		pptr->p_endpoint,
		pptr->p_nr,
		pptr->p_name);

		/* stores the socket address of the CONTROL channel of the process	*/
		memcpy((void*) &pptr->ctrl_addr,(void *)&pipc->addr,sizeof(struct sockaddr));

		/* builds the IPC message to return as a reply with the BIND Acknoledge	*/ 
		/* the process slot number and the process endpoint assigned by molkernel */
		pipc->molmsg.code = (BIND | ACKNOWLEDGE);
		pipc->molmsg.p_nr = pptr->p_nr;
		pipc->molmsg.p_endpoint = pptr->p_endpoint;
		}	
	else			/* Process Table FLLL */
		{
		pipc->molmsg.code = EMOLNOENT;
		MOLPRINT("Process table full: code=%d\n",EMOLNOENT);
		}

	/* Send the reply to the Minix Process */
      pipc->molmsg.spid = kpid;
      pipc->molmsg.seq 	= pptr->kseq++;
	rcode =svr_send(pipc);
	if( rcode != OK )
		MOLERROR("svr_send rcode=%d",rcode);  

	return (OK);	
}

/*----------------------------------------------------------*/
/*			do_unbind						*/
/* UNBIND request received						*/
/*----------------------------------------------------------*/
int do_unbind(ipc_t *pipc)
{
	int rcode, p_nr;
	struct kproc *pptr;

OLDEBUG("oper=%X spid=%d p_nr=%d seq=%d\n",
                pipc->molmsg.code,
                pipc->molmsg.spid,
                pipc->molmsg.p_nr,
                pipc->molmsg.seq);
	
	/* get the process slot pointer			*/
	pptr = &kproc[pipc->molmsg.p_nr+NR_TASKS];

	/* Check if the sender is a registered process 	*/
	rcode = check_registered(pptr, pipc);
	if( rcode != OK) return(rcode);

	/* unregister the process				*/
	pptr->p_rts_flags = SLOT_FREE;
	
	/* build and send the reply				*/	
	pipc->molmsg.code = (UNBIND | ACKNOWLEDGE);
      pipc->molmsg.spid = kpid;
	rcode =svr_send(pipc);
	if(rcode != OK )
		MOLERROR("svr_send rcode=%d",rcode);

	return(OK);
}

/*----------------------------------------------------------*/
/*			do_badrqst						*/
/* Bad request code received						*/
/*----------------------------------------------------------*/
int do_badrqst(ipc_t *pipc)
{
	int rcode;
OLDEBUG("oper=%X spid=%d p_nr=%d seq=%d\n",
                pipc->molmsg.code,
                pipc->molmsg.spid,
                pipc->molmsg.p_nr,
                pipc->molmsg.seq);
	
	/* build and send the reply				*/	
	pipc->molmsg.code = EMOLTRAPDENIED;
    	pipc->molmsg.spid = kpid;
	rcode =svr_send(pipc);
	if(rcode != OK )
		MOLERROR("svr_send rcode=%d",rcode);

	return(EMOLTRAPDENIED);                      
}

/*----------------------------------------------------------*/
/*			do_copyin				*/
/* send data to container				*/
/*----------------------------------------------------------*/
int do_copyin(ipc_t *pipc)
{
	int rcode;
	int nbytes;

	nbytes = pipc->molmsg.mnxmsg.m1_i1;
	
	if( nbytes > MAX_BLOCK_SIZE || nbytes < 0 )
			MOLERROR("COPYIN ERROR nbytes = %d\n",nbytes);

strncpy(&copybuf, "1234567890123456789", nbytes);  /* SOLO PARA PROBAR */

OLDEBUG("nbytes=%d data:%s\n", nbytes, copybuf);
		
	rcode = svr_data_send(pipc, (char*)&copybuf, nbytes);
	if(rcode != OK )
		MOLERROR("svr_data_send rcode=%d",rcode);
		
	return(OK);                      
}

/*----------------------------------------------------------*/
/*			do_copyout				*/
/* receive data from the container	      	*/
/*----------------------------------------------------------*/
int do_copyout(ipc_t *pipc)
{
	int rcode;
	int nbytes;

	nbytes = pipc->molmsg.mnxmsg.m1_i1;
	
	if( nbytes > MAX_BLOCK_SIZE || nbytes < 0 )
			MOLERROR("COPYOUT ERROR nbytes = %d\n",nbytes);

	
	rcode = svr_data_rcv(pipc, (char*)&copybuf, nbytes);
	if(rcode != OK )
		MOLERROR("svr_data_send rcode=%d",rcode);

/***************** ESTO NO VA ACA, PERO HAY QUE PONERLO ***********
	pipc->p_hello = 0;			
	pipc->p_dead = 0;			
*********************************************************************/

OLDEBUG("nbytes=%d data:%s\n", nbytes, copybuf);
		
	return(OK);                      
}

/*----------------------------------------------------------*/
/*			get_p_nr						*/
/* get process p_nr	(free or special )			*/
/*----------------------------------------------------------*/
int get_p_nr(ipc_t *pipc)
{
	int i;
	proc_nr_t p_nr;
	
OLDEBUG("oper=%X spid=%d p_nr=%d seq=%d\n",
                pipc->molmsg.code,
                pipc->molmsg.spid,
                pipc->molmsg.p_nr,
                pipc->molmsg.seq);

	/* Checks for special system processes		*/
	for(i = 0; i < NR_BOOT_PROCS ; i++)
		{
		if( strncmp((char*)&pipc->molmsg.mnxmsg.m_u.m_m3.m3ca1,
				(char*)name2nr[i].proc_name,
				P_NAME_LEN-1) == 0)
			{
OLDEBUG("find system process %s p_nr=%d\n",name2nr[i].proc_name,name2nr[i].proc_nr);
			if( kproc[name2nr[i].proc_nr+NR_TASKS].p_rts_flags != SLOT_FREE)
					MOLERROR("System Process slot not FREE: %d\n",NR_PROCS);
			return(name2nr[i].proc_nr);
			}
		}
		
	/* Searchs a free slot for a ordinary processes		*/ 
	for( i = (INIT_PROC_NR+1); i < NR_PROCS; i++)
		if( kproc[i+NR_TASKS].p_rts_flags == SLOT_FREE)
				return(i);

	MOLERROR("Process slots FULL: %d\n",NR_PROCS);                      
}

/*----------------------------------------------------------*/
/*		SIGNAL HANDLER SIGUSR1	      			*/
/* 	Print Process table with NO FREE entries			*/
/*----------------------------------------------------------*/
void sigusr1(int sig)
{
	int i, rcode;
	struct kproc *pptr;

	MOLPRINT("lxpid -kseq -pseq -p_nr endpt hello dead getfe sende name\n");
	for( i = 0; i < (NR_PROCS+NR_TASKS); i++)
		{
		pptr=&kproc[i];
		if( pptr->p_rts_flags != SLOT_FREE)
			{
			MOLPRINT("%05d %05ld %05ld %05d %05d %05d %04d %05d %05d %s\n",
				pptr->pid,
				pptr->kseq,
				pptr->pseq,
				pptr->p_nr,
				pptr->p_endpoint,
				pptr->p_hello,
				pptr->p_dead,		
				pptr->p_getfrom_e,
				pptr->p_sendto_e,
				pptr->p_name);
			/*----------------------------------- ESTO ES SOLO PARA PRUEBA DE COPYOUT ----------------
			if( strncmp(pptr->p_name,"MX",2) == 0)
				{
				pptr->p_hello = 0; 
				pptr->messbuf.m1_i1 = 20;
				rcode = send_container(pptr, COPYOUT);
				if (rcode == OK)
					MOLPRINT("send_container COPYOUT error: %d\n",rcode);
				}
			----------------------------------- ESTO ES SOLO PARA PRUEBA DE COPYOUT ----------------*/
			}
		}	
}	

/*----------------------------------------------------------*/
/*		 init_priv      						*/
/*----------------------------------------------------------*/
void init_priv()  
{ 
  register struct priv *sp;	/* privilege structure pointer */
  register int i;
OLDEBUG("\n");
	for (sp = BEG_PRIV_ADDR, i = 0; sp < END_PRIV_ADDR; ++sp, ++i) 
		{
		sp->s_proc_nr = NONE;			/* initialize as free */
		sp->s_id = i;				/* priv structure index */
		ppriv_addr[i] = sp;			/* priv ptr from number */
		}
}
  